# -*- coding: UTF-8 -*-
import json
import operator
import functools
from jsonpath_ng import parse


class JsonParse:
    """
    ---------------------------------------------------------------------
       JsonPath	                         说明
    ---------------------------------------------------------------------
       $	                文档根元素
       @	                当前元素
       .或[]	            匹配下级元素
       ..	                递归匹配所有子元素
       *	                通配符，匹配下级元素
       []	                下标运算符，根据索引获取元素，JsonPath索引从0开始
       [,]	                连接操作符，将多个结果拼接成数组返回，可以使用索引或别名
       [start:end:step]	    数据切片操作
       ?()	                过滤表达式
       ()	                脚本表达式，使用底层脚本引擎
    ---------------------------------------------------------------------
    """

    def __init__(self, json_data):
        self.__json = json_data

    def find_matches(self, expression):
        """
        查询指定表达式的内容
        :param expression: jsonpath表达式
        :return: 匹配的对象
        """
        parser = parse(expression)
        matches = parser.find(self.__json)
        return matches

    def delete(self, expression, value):
        """
        根据表达式匹配json内容，然后根据指定value删除key
        :param expression: jsonpath表达式
        :param value: 指定value
        :return: 过滤后的json内容
        """
        parser = parse(expression)
        parser.filter(lambda x: x == value, self.__json)
        return self.__json

    def update(self, expression, value):
        """
        根据表达式匹配json内容，修改value
        :param expression: jsonpath表达式
        :param value: 修改的内容
        :return: 修改后的json内容
        """
        parser = parse(expression)
        parser.update(self.__json, value)
        return self.__json

    def batch_updates(self, expressions: dict):
        """
        根据表达式批量修改json内容
        :param expressions: key为jsonpath表达式，value为修改后的内容
        """
        for expression, value in expressions.items():
            self.update(expression, value)

    def add(self, expression, value):
        """
        根据表达式新增数据
        :param expression: jsonpath表达式
        :param value: 新增内容
        :return: 新增后的json内容
        """
        parser = parse(expression)
        matches = parser.find_or_create(self.__json)
        for match in matches:
            if match.value == {}:
                # 更新原始输入的JSON数据
                match.full_path.update_or_create(self.__json, value)
        return self.__json

    def replace_value(self, key, value):
        """
        不限层级替换指定key的value,value的格式为#key#才会被替换-->{"folderId":"#folderId#"}
        :param key: 需要替换的key
        :param value: 替换的内容
        :return: 替换后的json
        """
        expression = f"$..{key}"
        matches = self.find_matches(expression)
        for matche in matches:
            if matche.value == f"#{key}#":
                matche.full_path.update(self.__json, value)
        return self.__json

    def batch_replace_values(self, kw: dict):
        """
        批量替换
        :param kw: 需要替换的key：value
        """
        for key, value in kw.items():
            self.replace_value(key, value)

    def query_same_level_value(self, expression, value, key):
        """
        根据表达式匹配值内容，再查询出同层级指定key的值
        :param expression:表达式
        :param value:匹配的值
        :param key:同层级指定的key
        :return:获取指定key的值
        """
        matches = self.find_matches(expression)
        for datumInContext in matches:
            if datumInContext.value == value:
                result = datumInContext.context.value.get(key)
                return result


    @staticmethod
    def assert_data_by_expression(expect: dict, result_data):
        """
        通过表达式-预期内容和实际结果断言
        :param expect: 字典,key为jsonpath表达式，value为预期的值。例:{"$.folderId":"0","$..elementName":["name1","name2".....]}
        :param result_data: 实际接口的返回内容
        """
        for expression, value in expect.items():
            # 根据表达式查询实际的结果
            matches = parse(expression).find(result_data)
            if isinstance(value, str):
                assert matches[0].value == value, f"预期内容【{value}】与实际内容【{matches[0].value}】不同"
            elif isinstance(value, list):
                result_value = [matche.value for matche in matches if matche.value]
                assert result_value == value, f"预期内容【{value}】与实际内容【{result_value}】不同"


def read_json(path, app_name: str = None, version: str = None):
    """
    解析用例参数
    :param path: json文件路径 path"
    :param app_name: 需要取值的key
    :param version: 版本，例:1.0
    :return: 读取内容
    """
    with open(path, 'r', encoding='utf-8') as load_f:
        load_dict = json.load(load_f)
    if app_name is None:
        return load_dict
    app = []
    if load_dict.get(app_name):
        return load_dict.get(app_name)
    elif load_dict.get('request'):
        request = load_dict.get('request').get(app_name)
        # 如果存在多版本
        if version:
            request = request.get(version)
        if isinstance(request, list):
            for i in request:
                """
                获取接口的参数等数据
                注释 请求报文 响应状态 响应消息 其他 期望结果
                """
                assertsOth = i.get("assert-other")
                # 用例跳过
                if assertsOth == "skip":
                    continue
                describes = i.get("describe-data")
                requests = i.get("request-data")
                assertsSta = i.get("assert-status")
                assertsMse = i.get("assert-message")
                expects = i.get("expect-data")
                if describes is None:
                    describes = "test case"
                params = (describes, requests, assertsSta, assertsMse, assertsOth, expects)
                app.append(params)
            return app
        else:
            return request
    else:
        return load_dict


def deal_assert_data(assertsOth: list = None, res=None):
    """
    断言响应的data值
    :param res: 接口响应的json
    :param assertsOth:
        查询data为list >>>>>>入参 [*, **] 断言 assert res.get("data")[0].get("*") **
            data:[
                    {
                    *: **
                    }
                ]
        查询data为int或单个str >>>>>>入参 ["", **] 断言 assert res.get("data") **
            data: *
        查询data为dict >>>>>>入参 [*, **] 断言 assert res.get("data").get("*") **
            data:{
                *: **
            }
    :return: True or False
    """
    if assertsOth:
        if assertsOth[0] == "":
            return operator.eq(res.json().get("data"), assertsOth[1])  # int
        elif type(res.json().get("data")) == dict:
            return operator.eq(res.json().get("data").get(assertsOth[0]), assertsOth[1])  # dict
        else:
            if operator.eq(res.json().get("data")[0].get(assertsOth[0]), assertsOth[1]):
                return True
            if assertsOth[1] in res.json().get("data")[0].get(assertsOth[0]):
                return True
            if assertsOth[1] is res.json().get("data")[0].get(assertsOth[0]):
                return True
    else:
        return True  # None


def deal_assert_status(json_, assertMsg):
    """
    断言响应消息
    :param json_: 响应消息
    :param assertMsg:
    :return:
    """
    if json_ is None:
        if json_ == assertMsg:
            return True
        else:
            return False
    else:
        if assertMsg in json_:
            return True
        elif json_ == assertMsg:
            return True
        else:
            return False


def deal_data(old_data, replace_key, replace_data):
    """
    传入未替换的json,返回替换json
    :param old_data: 旧json
    :param replace_key: 替换的匹配字段
    :param replace_data: 替换的值
    """
    new_body = str(old_data)
    for i in replace_key:
        if "'" in replace_data.get(i):
            replace_data.update({i: str(replace_data.get(i)).replace("'", r"\'")})
        new_body = new_body.replace("#{}#".format(i), r"{}".format(replace_data.get(i)))
    return eval(new_body)


def assert_dict_contain(expected_dict, result_dict):
    """
    判断预期的内容（字典）是否包含在实际的内容（字典）里
    :param expected_dict: 预期的内容
    :param result_dict: 实际的内容
    """
    result = None
    for key in expected_dict:
        if (key in result_dict) and (expected_dict[key] == result_dict[key]):
            result = True
        else:
            # print("没被包含的键值对为：", {key: expected_dict[key]})
            return False
    return result


def assert_dict_in_list(expected_dict, result_list):
    """
    判断expected_dict 是否in result_list
    :param expected_dict: 字典类型
    :param result_list: 列表类型
    """
    for result_dict in result_list:
        if assert_dict_contain(expected_dict, result_dict):
            return True
    return False


def assert_list_contain(expect_list, result_list):
    """
    判断expect_list in result_list
    :param expect_list: 列表类型里面都是字典
    :param result_list: 列表类型里面都是字典
    """
    result = None
    if expect_list == result_list:
        return True
    for expect_dict in expect_list:
        if assert_dict_in_list(expect_dict, result_list):
            result = True
        else:
            # print("没有被包含的在内的字典为：",expect_dict)
            return False
    return result


def dict_generator(indict, pre=None):
    """解析JSON"""
    pre = pre[:] if pre else []
    if isinstance(indict, dict):
        for key, value in indict.items():
            if isinstance(value, dict):
                if len(value) == 0:
                    yield pre + [key, '{}']
                else:
                    for d in dict_generator(value, pre + [key]):
                        yield d
            elif isinstance(value, list):
                if len(value) == 0:
                    yield pre + [key, '[]']
                else:
                    for v in value:
                        if isinstance(v, dict):
                            for d in dict_generator(v, pre + [key]):
                                yield d
                        else:
                            yield pre + [key, v]
            elif isinstance(value, tuple):
                if len(value) == 0:
                    yield pre + [key, '()']
                else:
                    for v in value:
                        for d in dict_generator(v, pre + [key]):
                            yield d
            else:
                yield pre + [key, value]
    else:
        yield indict


def replace_json_value(json_dict, search_key, replaceStr):
    """
    替换dict指定key的value（不限层级）
    :param json_dict: 需要替换的对象
    :param search_key: 需要替换的key
    :param replaceStr: 替换的内容
    """
    for key in json_dict.keys():
        if type(json_dict[key]) is dict:
            replace_json_value(json_dict[key], search_key, replaceStr)
        elif type(json_dict[key]) is list:
            for one in json_dict[key]:
                if type(one) is dict:
                    replace_json_value(one, search_key, replaceStr)
        else:
            if key == search_key:
                json_dict[key] = replaceStr


def replace_json_for_value(json_dict: dict, search_value, replaceStr):
    """
    替换dict指定的value（不限层级）
    :param json_dict: 需要替换的对象
    :param search_value: 需要替换的value
    :param replaceStr: 替换的内容
    """
    for key, value in json_dict.items():
        if type(json_dict[key]) is dict:
            replace_json_for_value(json_dict[key], search_value, replaceStr)
        elif type(json_dict[key]) is list:
            for one in json_dict[key]:
                if type(one) is dict:
                    replace_json_for_value(one, search_value, replaceStr)
        else:
            if value == search_value:
                json_dict[key] = replaceStr


def list_dict_duplicate_removal(data_list):
    """
    列表里去掉重复字典
    """
    run_function = lambda x, y: x if y in x else x + [y]
    return functools.reduce(run_function, [[], ] + data_list)


def unpack_query_result_to_str(query_result):
    """
    sql查询结果转成字符串
    :param query_result: sql查询结果
    """
    if query_result:
        tup_result = functools.reduce(lambda x, y: x + y, query_result)
        if len(tup_result) > 1:
            str_result = str(tup_result)
        else:
            str_result = str(tup_result).replace(",", "")
        return str_result


if __name__ == '__main__':
    pass
